"""
设备管理器
整合设备发现和监控功能，统一管理ONVIF设备
"""

from datetime import datetime
from typing import Dict, List, Optional
import json
from pathlib import Path

from .discovery.ws_discovery import DiscoveryService
from .monitoring.network_monitor import DeviceMonitor
from .monitoring.prometheus_exporter import PrometheusExporter
from .monitoring.device_status_manager import DeviceStatusManager
from .models.device import DeviceInfo, DeviceStatus, DeviceStatusRecord, NetworkMetrics
from .utils.config import Config
from .utils.logger import get_logger

logger = get_logger(__name__)


class DeviceManager:
    """设备管理器"""
    
    def __init__(self, config: Optional[Config] = None):
        self.config = config or Config()
        
        # 初始化服务
        self.discovery_service = DiscoveryService(
            self.config.discovery.multicast_address,
            self.config.discovery.multicast_port
        )
        
        self.device_monitor = DeviceMonitor(
            ping_timeout=self.config.monitoring.ping.timeout,
            ping_count=self.config.monitoring.ping.packet_count,
            port_timeout=self.config.monitoring.port_check.timeout
        )

        # 初始化增强设备状态管理器
        self.device_status_manager = DeviceStatusManager(self.config)

        # 初始化Prometheus导出器
        self.prometheus_exporter = None
        if hasattr(self.config, 'prometheus') and self.config.prometheus.enabled:
            try:
                self.prometheus_exporter = PrometheusExporter(
                    port=self.config.prometheus.port,
                    host=self.config.prometheus.host
                )
                self.prometheus_exporter.start_server()
                logger.info(f"Prometheus指标服务器已启动: http://{self.config.prometheus.host}:{self.config.prometheus.port}/metrics")
            except Exception as e:
                logger.error(f"启动Prometheus导出器失败: {e}")
                self.prometheus_exporter = None

        # 设备存储
        self.devices: Dict[str, DeviceInfo] = {}  # device_id -> DeviceInfo
        self.status_history: Dict[str, List[DeviceStatusRecord]] = {}  # device_id -> status records

        # 离线设备管理
        self.offline_devices: Dict[str, DeviceInfo] = {}  # device_id -> DeviceInfo (离线设备)
        self.offline_timestamps: Dict[str, datetime] = {}  # device_id -> 离线时间戳
        self.offline_retention_minutes = config.monitoring.offline_devices.retention_minutes  # 从配置文件读取

        # 数据持久化文件
        self.data_file = Path("devices.json")

        # 加载已保存的设备数据
        self._load_devices()

    def _handle_device_offline(self, device_id: str, device: DeviceInfo, error_message: str = ""):
        """
        处理设备离线状态

        Args:
            device_id: 设备ID
            device: 设备信息
            error_message: 错误信息
        """
        logger.warning(f"设备离线: {device.ip_address} ({device_id})")

        # 更新设备状态为离线
        device.status = DeviceStatus.OFFLINE

        # 创建离线状态记录
        offline_record = DeviceStatusRecord(
            device_id=device_id,
            status=DeviceStatus.OFFLINE,
            error_message=error_message or "设备无响应"
        )

        # 保存状态历史
        if device_id not in self.status_history:
            self.status_history[device_id] = []
        self.status_history[device_id].append(offline_record)

        # 立即向Prometheus汇报离线状态
        if self.prometheus_exporter:
            self.prometheus_exporter.update_device_status(device, offline_record)
            logger.info(f"已向Prometheus汇报设备离线: {device.ip_address}")

        # 将设备移到离线设备列表
        self.offline_devices[device_id] = device
        self.offline_timestamps[device_id] = datetime.now()

        # 从在线设备列表中移除
        if device_id in self.devices:
            del self.devices[device_id]

        logger.info(f"设备 {device.ip_address} 已标记为离线，将保留信息5分钟")

    def _handle_device_online(self, device_id: str, device: DeviceInfo, status_record: DeviceStatusRecord):
        """
        处理设备上线状态

        Args:
            device_id: 设备ID
            device: 设备信息
            status_record: 状态记录
        """
        # 如果设备之前在离线列表中，将其移回在线列表
        if device_id in self.offline_devices:
            logger.info(f"设备重新上线: {device.ip_address} ({device_id})")
            del self.offline_devices[device_id]
            del self.offline_timestamps[device_id]

        # 确保设备在在线列表中
        self.devices[device_id] = device

        # 更新Prometheus指标
        if self.prometheus_exporter:
            self.prometheus_exporter.update_device_status(device, status_record)

    def _cleanup_offline_devices(self):
        """
        清理过期的离线设备信息
        """
        current_time = datetime.now()
        expired_devices = []

        for device_id, offline_time in self.offline_timestamps.items():
            if (current_time - offline_time).total_seconds() > (self.offline_retention_minutes * 60):
                expired_devices.append(device_id)

        for device_id in expired_devices:
            if device_id in self.offline_devices:
                device = self.offline_devices[device_id]
                logger.info(f"清理过期离线设备: {device.ip_address} ({device_id})")

                # 从Prometheus中移除设备指标
                if self.prometheus_exporter:
                    # 注意：Prometheus指标通常不会自动过期
                    # 这里可以考虑设置一个特殊的状态来标记设备已被清理
                    logger.debug(f"设备 {device.ip_address} 的Prometheus指标将保持最后状态")

                del self.offline_devices[device_id]
                del self.offline_timestamps[device_id]

                # 也清理状态历史
                if device_id in self.status_history:
                    del self.status_history[device_id]

        if expired_devices:
            logger.info(f"已清理 {len(expired_devices)} 个过期离线设备")
    
    def discover_devices(self, timeout: Optional[int] = None) -> List[DeviceInfo]:
        """
        发现网络中的ONVIF设备

        Args:
            timeout: 发现超时时间，默认使用配置文件中的值

        Returns:
            List[DeviceInfo]: 发现的设备列表
        """
        timeout = timeout or self.config.discovery.timeout

        logger.info("开始发现ONVIF设备...")
        start_time = datetime.now()

        discovered_devices = self.discovery_service.discover_all_devices(timeout)

        # 记录发现耗时
        discovery_duration = (datetime.now() - start_time).total_seconds()
        if self.prometheus_exporter:
            self.prometheus_exporter.record_discovery_duration(discovery_duration)
            self.prometheus_exporter.record_devices_discovered(len(discovered_devices))

        # 更新设备列表
        for device in discovered_devices:
            if device.device_id not in self.devices:
                # 新发现的设备
                self.devices[device.device_id] = device
                self.status_history[device.device_id] = []
                logger.info(f"新发现设备: {device.ip_address} ({device.device_id})")

                # 更新Prometheus设备信息指标
                if self.prometheus_exporter:
                    self.prometheus_exporter.update_device_info(device)
            else:
                # 更新已存在设备的信息
                existing_device = self.devices[device.device_id]
                existing_device.last_seen = datetime.now()
                existing_device.endpoint_url = device.endpoint_url
                logger.info(f"更新设备信息: {device.ip_address} ({device.device_id})")

                # 更新Prometheus设备信息指标
                if self.prometheus_exporter:
                    self.prometheus_exporter.update_device_info(existing_device)

        # 保存设备数据
        self._save_devices()

        logger.info(f"设备发现完成，共发现 {len(discovered_devices)} 个设备")
        return discovered_devices
    
    def probe_device(self, ip_address: str, timeout: Optional[int] = None) -> Optional[DeviceInfo]:
        """
        探测指定IP的ONVIF设备
        
        Args:
            ip_address: 目标IP地址
            timeout: 探测超时时间
            
        Returns:
            Optional[DeviceInfo]: 设备信息，如果探测失败则返回None
        """
        timeout = timeout or self.config.discovery.timeout
        
        logger.info(f"探测指定设备: {ip_address}")
        device = self.discovery_service.probe_device(ip_address, timeout)
        
        if device:
            if device.device_id not in self.devices:
                self.devices[device.device_id] = device
                self.status_history[device.device_id] = []
                logger.info(f"探测到新设备: {ip_address} ({device.device_id})")
            else:
                # 更新设备信息
                existing_device = self.devices[device.device_id]
                existing_device.last_seen = datetime.now()
                logger.info(f"更新设备信息: {ip_address} ({device.device_id})")
            
            self._save_devices()
        
        return device
    
    def monitor_device(self, device_id: str, use_enhanced_check: bool = True) -> Optional[DeviceStatusRecord]:
        """
        监控指定设备的状态（支持快速离线检测）

        Args:
            device_id: 设备ID
            use_enhanced_check: 是否使用增强检测（快速离线检测）

        Returns:
            Optional[DeviceStatusRecord]: 设备状态记录
        """
        # 检查设备是否存在（包括离线设备）
        device = None
        if device_id in self.devices:
            device = self.devices[device_id]
        elif device_id in self.offline_devices:
            device = self.offline_devices[device_id]
        else:
            logger.warning(f"设备不存在: {device_id}")
            return None

        logger.info(f"监控设备: {device.ip_address} ({device_id}) - 增强检测: {use_enhanced_check}")

        if use_enhanced_check:
            # 使用增强设备状态管理器进行检测
            health_status = self.device_status_manager.check_device(device)

            # 更新设备基本信息
            device.status = health_status.status
            device.last_seen = health_status.last_check_time

            # 创建状态记录
            status_record = DeviceStatusRecord(
                device_id=device_id,
                status=health_status.status,
                network_metrics=health_status.network_metrics,
                error_message="; ".join(health_status.failure_reasons[-1:]) if health_status.failure_reasons else ""
            )

            # 保存状态历史
            if device_id not in self.status_history:
                self.status_history[device_id] = []
            self.status_history[device_id].append(status_record)

            # 限制历史记录数量（保留最近100条）
            if len(self.status_history[device_id]) > 100:
                self.status_history[device_id] = self.status_history[device_id][-100:]

            # 根据状态判断是在线还是离线
            if health_status.status == DeviceStatus.OFFLINE:
                # 设备离线，使用离线处理逻辑
                failure_reason = "; ".join(health_status.failure_reasons[-3:]) if health_status.failure_reasons else "快速检测确认离线"
                self._handle_device_offline(device_id, device, failure_reason)
            else:
                # 设备在线或有问题但未完全离线，使用在线处理逻辑
                self._handle_device_online(device_id, device, status_record)

            # 向Prometheus汇报状态
            if self.prometheus_exporter:
                self.prometheus_exporter.update_device_status(device, status_record)

            logger.info(f"设备 {device.ip_address} 增强监控完成 - 状态: {health_status.status.value}")
            return status_record

        else:
            # 使用传统监控方式
            monitor_result = self.device_monitor.monitor_device(
                device.ip_address,
                self.config.monitoring.port_check.ports
            )

        if monitor_result['success']:
            # 创建网络指标对象
            metrics_data = monitor_result['network_metrics']
            network_metrics = NetworkMetrics(
                latency_ms=metrics_data['latency_ms'],
                packet_loss_rate=metrics_data['packet_loss_rate'],
                jitter_ms=metrics_data['jitter_ms'],
                timestamp=datetime.fromisoformat(metrics_data['timestamp'])
            )

            # 更新设备状态
            new_status = DeviceStatus(monitor_result['status'])
            device.status = new_status
            device.last_seen = datetime.now()

            # 创建状态记录
            status_record = DeviceStatusRecord(
                device_id=device_id,
                status=new_status,
                network_metrics=network_metrics,
                open_ports=monitor_result['open_ports']
            )

            # 保存状态历史
            if device_id not in self.status_history:
                self.status_history[device_id] = []
            self.status_history[device_id].append(status_record)

            # 限制历史记录数量（保留最近100条）
            if len(self.status_history[device_id]) > 100:
                self.status_history[device_id] = self.status_history[device_id][-100:]

            # 根据状态判断是在线还是离线
            if new_status == DeviceStatus.OFFLINE:
                # 设备离线，使用离线处理逻辑
                self._handle_device_offline(device_id, device, monitor_result.get('error_message', ''))
            else:
                # 设备在线，使用在线处理逻辑
                self._handle_device_online(device_id, device, status_record)

            logger.info(f"设备 {device.ip_address} 监控完成 - 状态: {new_status.value}")
            return status_record

        else:
            # 监控失败，认为设备离线
            logger.warning(f"设备监控失败，标记为离线: {device.ip_address}")

            # 使用离线处理逻辑
            self._handle_device_offline(device_id, device, monitor_result['error_message'])

            # 创建离线状态记录
            status_record = DeviceStatusRecord(
                device_id=device_id,
                status=DeviceStatus.OFFLINE,
                error_message=monitor_result['error_message']
            )

            return status_record
    
    def monitor_all_devices(self, use_enhanced_check: bool = True, max_workers: int = 10) -> List[DeviceStatusRecord]:
        """
        监控所有设备的状态（支持快速离线检测）

        Args:
            use_enhanced_check: 是否使用增强检测（快速离线检测）
            max_workers: 最大并发数

        Returns:
            List[DeviceStatusRecord]: 所有设备的状态记录
        """
        # 清理过期的离线设备
        self._cleanup_offline_devices()

        total_devices = len(self.devices) + len(self.offline_devices)
        logger.info(f"开始监控所有设备，在线设备: {len(self.devices)}, 离线设备: {len(self.offline_devices)}, 总计: {total_devices} - 增强检测: {use_enhanced_check}")

        if use_enhanced_check and total_devices > 0:
            # 使用增强批量检测
            all_devices = list(self.devices.values()) + list(self.offline_devices.values())
            health_statuses = self.device_status_manager.check_devices_batch(all_devices, max_workers)

            results = []
            for device_id, health_status in health_statuses.items():
                # 获取设备信息
                device = self.devices.get(device_id) or self.offline_devices.get(device_id)
                if not device:
                    continue

                # 更新设备基本信息
                device.status = health_status.status
                device.last_seen = health_status.last_check_time

                # 创建状态记录
                status_record = DeviceStatusRecord(
                    device_id=device_id,
                    status=health_status.status,
                    network_metrics=health_status.network_metrics,
                    error_message="; ".join(health_status.failure_reasons[-1:]) if health_status.failure_reasons else ""
                )

                # 保存状态历史
                if device_id not in self.status_history:
                    self.status_history[device_id] = []
                self.status_history[device_id].append(status_record)

                # 限制历史记录数量
                if len(self.status_history[device_id]) > 100:
                    self.status_history[device_id] = self.status_history[device_id][-100:]

                # 处理设备状态变化
                if health_status.status == DeviceStatus.OFFLINE:
                    failure_reason = "; ".join(health_status.failure_reasons[-3:]) if health_status.failure_reasons else "批量检测确认离线"
                    self._handle_device_offline(device_id, device, failure_reason)
                else:
                    self._handle_device_online(device_id, device, status_record)

                # 向Prometheus汇报状态
                if self.prometheus_exporter:
                    self.prometheus_exporter.update_device_status(device, status_record)

                results.append(status_record)

        else:
            # 使用传统方式逐个监控
            results = []

            # 监控在线设备
            device_ids_to_monitor = list(self.devices.keys())  # 创建副本，因为字典可能在迭代中被修改
            for device_id in device_ids_to_monitor:
                result = self.monitor_device(device_id, use_enhanced_check=False)
                if result:
                    results.append(result)

            # 也尝试监控离线设备（检查是否重新上线）
            offline_device_ids = list(self.offline_devices.keys())
            for device_id in offline_device_ids:
                logger.debug(f"检查离线设备是否重新上线: {device_id}")
                result = self.monitor_device(device_id, use_enhanced_check=False)
                if result:
                    results.append(result)

        # 保存设备数据
        self._save_devices()

        # 统计结果
        online_count = sum(1 for r in results if r.status == DeviceStatus.ONLINE)
        offline_count = sum(1 for r in results if r.status == DeviceStatus.OFFLINE)
        other_count = len(results) - online_count - offline_count

        # 更新Prometheus系统级指标
        if self.prometheus_exporter:
            stats = self.get_statistics()
            self.prometheus_exporter.update_system_metrics(stats)
            self.prometheus_exporter.record_monitoring_cycle()

        logger.info(f"所有设备监控完成 - 在线: {online_count}, 离线: {offline_count}, 其他: {other_count}")
        return results
    
    def get_device_list(self) -> List[DeviceInfo]:
        """获取设备列表"""
        return list(self.devices.values())
    
    def get_device(self, device_id: str) -> Optional[DeviceInfo]:
        """获取指定设备信息"""
        return self.devices.get(device_id)
    
    def get_device_by_ip(self, ip_address: str) -> Optional[DeviceInfo]:
        """根据IP地址获取设备"""
        for device in self.devices.values():
            if device.ip_address == ip_address:
                return device
        return None
    
    def get_device_status_history(self, device_id: str, limit: int = 10) -> List[DeviceStatusRecord]:
        """获取设备状态历史"""
        if device_id not in self.status_history:
            return []
        
        history = self.status_history[device_id]
        return history[-limit:] if limit > 0 else history
    
    def get_online_devices(self) -> List[DeviceInfo]:
        """获取在线设备列表"""
        return [device for device in self.devices.values() if device.status == DeviceStatus.ONLINE]
    
    def get_offline_devices(self) -> List[DeviceInfo]:
        """获取离线设备列表"""
        return [device for device in self.devices.values() if device.status == DeviceStatus.OFFLINE]
    
    def remove_device(self, device_id: str) -> bool:
        """移除设备"""
        device = None

        # 从在线设备中查找
        if device_id in self.devices:
            device = self.devices[device_id]
            del self.devices[device_id]

        # 从离线设备中查找
        if device_id in self.offline_devices:
            if not device:  # 如果在线设备中没找到，从离线设备中获取
                device = self.offline_devices[device_id]
            del self.offline_devices[device_id]
            if device_id in self.offline_timestamps:
                del self.offline_timestamps[device_id]

        # 清理状态历史
        if device_id in self.status_history:
            del self.status_history[device_id]

        if device:
            # 清理Prometheus metrics
            if self.prometheus_exporter:
                self.prometheus_exporter.clear_device_metrics(device)

            self._save_devices()
            logger.info(f"已移除设备: {device.ip_address} ({device_id})")
            return True

        logger.warning(f"未找到要移除的设备: {device_id}")
        return False
    
    def clear_all_devices(self):
        """清空所有设备"""
        logger.info("开始清空所有设备...")

        # 清理Prometheus metrics
        if self.prometheus_exporter:
            logger.info("清理Prometheus设备metrics...")
            self.prometheus_exporter.clear_all_device_metrics()

        # 清理内存中的设备数据
        self.devices.clear()
        self.status_history.clear()

        # 清理离线设备数据
        self.offline_devices.clear()
        self.offline_timestamps.clear()

        # 清理发现服务的缓存
        self.discovery_service.clear_discovered_devices()

        # 保存空的设备数据到文件
        self._save_devices()

        # 更新系统级metrics为0
        if self.prometheus_exporter:
            stats = self.get_statistics()
            self.prometheus_exporter.update_system_metrics(stats)

        logger.info("已清空所有设备")
    
    def get_statistics(self) -> dict:
        """获取统计信息"""
        # 包含在线和离线设备的总数
        total_devices = len(self.devices) + len(self.offline_devices)
        online_devices = len(self.get_online_devices())

        # 离线设备包括在offline_devices中的设备和在线设备中状态为OFFLINE的设备
        offline_from_online = len([d for d in self.devices.values() if d.status == DeviceStatus.OFFLINE])
        offline_devices = len(self.offline_devices) + offline_from_online

        # 统计各种状态的设备数量（仅统计在线设备列表中的）
        network_issue_devices = len([d for d in self.devices.values() if d.status == DeviceStatus.NETWORK_ISSUE])
        service_issue_devices = len([d for d in self.devices.values() if d.status == DeviceStatus.SERVICE_ISSUE])
        unknown_devices = len([d for d in self.devices.values() if d.status == DeviceStatus.UNKNOWN])

        return {
            'total_devices': total_devices,
            'online_devices': online_devices,
            'offline_devices': offline_devices,
            'network_issue_devices': network_issue_devices,
            'service_issue_devices': service_issue_devices,
            'unknown_devices': unknown_devices,
            'uptime_percentage': (online_devices / total_devices * 100) if total_devices > 0 else 0,
            'offline_retention_count': len(self.offline_devices),  # 保留中的离线设备数量
            'offline_retention_minutes': self.offline_retention_minutes  # 离线保留时间
        }
    
    def _save_devices(self):
        """保存设备数据到文件"""
        try:
            data = {
                'devices': {device_id: device.to_dict() for device_id, device in self.devices.items()},
                'offline_devices': {device_id: device.to_dict() for device_id, device in self.offline_devices.items()},
                'offline_timestamps': {device_id: timestamp.isoformat() for device_id, timestamp in self.offline_timestamps.items()},
                'last_updated': datetime.now().isoformat()
            }

            with open(self.data_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=2, ensure_ascii=False)

            logger.debug(f"设备数据已保存到 {self.data_file} (在线: {len(self.devices)}, 离线: {len(self.offline_devices)})")

        except Exception as e:
            logger.error(f"保存设备数据失败: {e}")
    
    def _load_devices(self):
        """从文件加载设备数据"""
        try:
            if not self.data_file.exists():
                logger.info("设备数据文件不存在，使用空设备列表")
                return

            with open(self.data_file, 'r', encoding='utf-8') as f:
                data = json.load(f)

            # 加载在线设备信息
            devices_data = data.get('devices', {})
            for device_id, device_data in devices_data.items():
                device = DeviceInfo.from_dict(device_data)
                self.devices[device_id] = device

                # 初始化状态历史
                if device_id not in self.status_history:
                    self.status_history[device_id] = []

            # 加载离线设备信息
            offline_devices_data = data.get('offline_devices', {})
            for device_id, device_data in offline_devices_data.items():
                device = DeviceInfo.from_dict(device_data)
                self.offline_devices[device_id] = device

                # 初始化状态历史
                if device_id not in self.status_history:
                    self.status_history[device_id] = []

            # 加载离线时间戳
            offline_timestamps_data = data.get('offline_timestamps', {})
            for device_id, timestamp_str in offline_timestamps_data.items():
                try:
                    self.offline_timestamps[device_id] = datetime.fromisoformat(timestamp_str)
                except ValueError as e:
                    logger.warning(f"解析离线时间戳失败 {device_id}: {e}")

            logger.info(f"从 {self.data_file} 加载了 {len(self.devices)} 个在线设备, {len(self.offline_devices)} 个离线设备")

        except Exception as e:
            logger.error(f"加载设备数据失败: {e}")
            self.devices = {}
            self.offline_devices = {}
            self.offline_timestamps = {}
            self.status_history = {}
